library(dplyr)
library(tidyr)
library(readr)
library(ggplot2)
library(lattice)
library(naniar)
library(skimr)

Introduction

Data Preparation

Read In

input_dir <- fs::path("../input")
files <- fs::dir_ls(input_dir, glob = "*.csv")
his_dt <- read_csv(
  files[1],
  col_types = cols(
    package = col_character(),
    version = col_character(),
    date = col_date(format = "%Y-%m-%d"),
    repository = col_character()
  )
)
ov_dt <- read_csv(
  files[2],
  col_types = cols(
    package = col_character(),
    version = col_character(),
    depends = col_character(),
    imports = col_character(),
    license = col_character(),
    needs_compilation = col_logical(),
    author = col_character(),
    bug_reports = col_character(),
    url = col_character(),
    date_published = col_date(format = "%Y-%m-%d"),
    description = col_character(),
    title = col_character()
  )
)

Quick View

dplyr::glimpse(ov_dt, 100)
Rows: 18,388
Columns: 12
$ package           <chr> "A3", "AATtools", "ABACUS", "abbreviate", "abbyyR", "abc", "abc.data", "…
$ version           <chr> "1.0.0", "0.0.1", "1.0.0", "0.1", "0.5.5", "2.2.1", "1.0", "0.9.0", "1.0…
$ depends           <chr> "R (>= 2.15.0), xtable, pbapply", "R (>= 3.6.0)", "R (>= 3.1.0)", NA, "R…
$ imports           <chr> NA, "magrittr, dplyr, doParallel, foreach", "ggplot2 (>= 3.1.0), shiny (…
$ license           <chr> "GPL (>= 2)", "GPL-3", "GPL-3", "GPL-3", "MIT + file LICENSE", "GPL (>= …
$ needs_compilation <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRU…
$ author            <chr> "Scott Fortmann-Roe", "Sercan Kahveci [aut, cre]", "Mintu Nath [aut, cre…
$ bug_reports       <chr> NA, "https://github.com/Spiritspeak/AATtools/issues", NA, NA, "http://gi…
$ url               <chr> NA, NA, "https://shiny.abdn.ac.uk/Stats/apps/", "https://github.com/sigb…
$ date_published    <date> 2015-08-16, 2020-06-14, 2019-09-20, 2021-12-14, 2019-06-25, 2022-05-19,…
$ description       <chr> "Supplies tools for tabulating and analyzing the results of predictive m…
$ title             <chr> "Accurate, Adaptable, and Accessible Error Metrics for Predictive\nModel…

Data Quality

ov_dt |> 
  dplyr::arrange(date_published) |> 
  vis_miss()

Questions

Features

Separate version

ov_dt <- ov_dt |>
  separate(
    version,
    into =
      c("major", "minor", "patch"),
    sep = "\\.",
    extra = "merge",
    fill = "right",
    remove = FALSE
  )

Number of dependencies

ov_dt <- ov_dt |> 
  mutate(
    num_dep = purrr::map_int(
      .x = depends,
      .f = function(x){
        x |> 
          stringr::str_split(",", simplify = TRUE) |> 
          length()
      }
    ),
    num_dep = ifelse(is.na(depends), 0, num_dep)
  )

Number of imports

ov_dt <- ov_dt |> 
  mutate(
    num_imports = purrr::map_int(
      .x = imports,
      .f = function(x){
        x |> 
          stringr::str_split(",", simplify = TRUE) |> 
          length()
      }
    ),
    num_imports = ifelse(is.na(imports), 0, num_imports)
  )

Number of authors

ov_dt <- ov_dt |> 
  mutate(
    num_authors = purrr::map_int(
      .x = author,
      .f = function(x){
        x |> 
          stringr::str_split(",", simplify = TRUE) |> 
          length()
      }
    )
  )

Temporal features

ov_dt <- ov_dt |> 
  mutate(
    year = lubridate::year(date_published),
    month = lubridate::month(date_published, label = TRUE),
    day = lubridate::day(date_published),
    wday = lubridate::wday(date_published, label = TRUE),
    yr_mon = sprintf("%d-%s", year, month),
    dt = lubridate::ym(paste0(year, "-", month))
  )
Warning:  1 failed to parse.

Title & Description Lengths

ov_dt <- ov_dt |>
  mutate(
    len_title = purrr::map_int(title, ~ stringr::str_count(.x, "\\w+")),
    len_desc = purrr::map_int(description, ~ stringr::str_count(.x, "\\w+"))
  )

License

ov_dt <- ov_dt |> 
  mutate(
    license_cleaned = case_when(
      stringr::str_detect(license, "^GPL-3") ~ "GPL-3",
      stringr::str_detect(license, "^GPL\\s\\([\\s\\d\\.<=>]*3") ~ "GPL-3",
      stringr::str_detect(license, "^GPL-2") ~ "GPL-2",
      stringr::str_detect(license, "^GPL\\s\\([\\s\\d\\.<=>]*2") ~ "GPL-2",
      stringr::str_detect(license, "^AGPL") ~ "AGPL",
      stringr::str_detect(license, "^LGPL") ~ "LGPL",
      stringr::str_detect(license, "Apache") ~ "Apache",
      stringr::str_detect(license, "BSD") ~ "BSD",
      stringr::str_detect(license, "LGPL") ~ "LGPL",
      # stringr::str_detect(license, "GNU") ~ "GNU",
      stringr::str_detect(license, "MIT") ~ "MIT",
      stringr::str_detect(license, "CC0") ~ "CC0",
      # stringr::str_detect(license, "MPL") ~ "MPL",
      # stringr::str_detect(license, "Unlimited") ~ "Unlimited",
      # stringr::str_detect(license, "^CC") ~ "CC",
      license == "GPL" ~ "GPL",
      TRUE ~ "Other"
      )
  )

Bug Report Domain

Temporal Questions

ov_dt |> 
    group_by(dt) |> 
    summarise_at(vars(num_dep, num_imports), list(mean = mean)) |> 
  ggplot(aes(x= dt)) +
    geom_jitter(aes(y = num_dep_mean, color = "num_dep_mean"), alpha = 0.2) +
  geom_smooth(aes(y = num_dep_mean, color = "num_dep_mean"), span = 0.3, se = FALSE) +
  geom_jitter(aes(y = num_imports_mean, color = "num_imports_mean"), alpha = 0.2) +
  geom_smooth(aes(y = num_imports_mean, color = "num_imports_mean"), span = 0.3, se = FALSE) +
  theme_light()

ov_dt |> 
    group_by(dt) |> 
    summarise_at(vars(len_title, len_desc), list(median = median, sd = sd), na.rm = TRUE) |> 
  ggplot(aes(x= dt)) +
    geom_jitter(aes(y = len_title_median, color = "len_title_median"), alpha = 0.2) +
  geom_smooth(aes(y = len_title_median, color = "len_title_median"), span = 0.3, se = FALSE) +
  geom_jitter(aes(y = len_desc_median, color = "len_desc_median"), alpha = 0.2) +
  geom_smooth(aes(y = len_desc_median, color = "len_desc_median"), span = 0.3, se = FALSE) +
  theme_light()

ov_dt |> 
  ggplot(aes(x= date_published, y = len_title)) +
  geom_jitter(alpha = 0.05) +
  geom_smooth(span = 0.1, se = FALSE) +
  theme_light() +
  scale_y_log10()


ov_dt |> 
  ggplot(aes(x= date_published, y = len_desc)) +
  geom_jitter(alpha = 0.05) +
  geom_smooth(span = 0.2, se = FALSE) +
  theme_light() +
  scale_y_log10()

ov_dt |> 
  group_by(license_cleaned) |> 
  count() |> 
  ggplot(aes(x = forcats::fct_reorder(license_cleaned, n), y = n, fill = license_cleaned)) +
  geom_col() +
  coord_flip() +
  theme_minimal() +
  guides(fill = FALSE) +
  labs(x = "", y = "")
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

ov_dt |> 
  group_by(dt) |> 
  count(license_cleaned) |> 
  mutate(license_cleaned = forcats::fct_reorder(license_cleaned, n)) |> 
  ggplot(aes(x= dt, y = n, color = license_cleaned)) +
  # geom_line( alpha = 0.3) +
  geom_jitter(alpha = 0.3) +
  geom_smooth(span = 0.3, se = FALSE) +
  theme_light()

ov_dt |> 
  group_by(dt) |> 
  count(license_cleaned) |> 
  mutate(license_cleaned = forcats::fct_reorder(license_cleaned, n)) |> 
  ggplot(aes(x= dt, y = n, color = license_cleaned)) +
  # geom_line( alpha = 0.3) +
  geom_jitter(alpha = 0.3) +
  geom_smooth(span = 0.8, se = FALSE) +
  theme_light() +
  scale_y_log10()

ov_dt |> 
  group_by(dt) |> 
  count(url_exist = is.na(url)) |>  
  ggplot(aes(x= dt, y = n, color = url_exist)) +
  geom_jitter(alpha = 0.3) +
  geom_smooth(span = 0.3, se = FALSE) +
  theme_light()

ov_dt |> 
  group_by(dt) |> 
  count(url_exist = is.na(bug_reports)) |>  
  ggplot(aes(x= dt, y = n, color = url_exist)) +
  geom_jitter(alpha = 0.3) +
  geom_smooth(span = 0.3, se = FALSE) +
  theme_light()

ov_dt |> 
  group_by(dt) |> 
  count() |> 
  ggplot(aes(dt, n)) +
  geom_line()

ov_dt |> filter(!is.na(dt)) |> count(dt) |> arrange(dt) |> timetk::pad_by_time(.by = "month", .pad_value = 0) -> xdat
.date_var is missing. Using: dt
timetk::plot_seasonal_diagnostics(xdat, dt, n)
LS0tCnRpdGxlOiAiQ1JBTiBIaXN0b3J5IEVEQSIKYXV0aG9yOiAiUiBTYW5nb2xlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBsaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsYXR0aWNlKQpsaWJyYXJ5KG5hbmlhcikKbGlicmFyeShza2ltcikKYGBgCgojIEludHJvZHVjdGlvbgoKCgojIERhdGEgUHJlcGFyYXRpb24gey50YWJzZXR9CgojIyBSZWFkIEluIHsudGFic2V0fQoKYGBge3J9CmlucHV0X2RpciA8LSBmczo6cGF0aCgiLi4vaW5wdXQiKQpmaWxlcyA8LSBmczo6ZGlyX2xzKGlucHV0X2RpciwgZ2xvYiA9ICIqLmNzdiIpCmhpc19kdCA8LSByZWFkX2NzdigKICBmaWxlc1sxXSwKICBjb2xfdHlwZXMgPSBjb2xzKAogICAgcGFja2FnZSA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIHZlcnNpb24gPSBjb2xfY2hhcmFjdGVyKCksCiAgICBkYXRlID0gY29sX2RhdGUoZm9ybWF0ID0gIiVZLSVtLSVkIiksCiAgICByZXBvc2l0b3J5ID0gY29sX2NoYXJhY3RlcigpCiAgKQopCm92X2R0IDwtIHJlYWRfY3N2KAogIGZpbGVzWzJdLAogIGNvbF90eXBlcyA9IGNvbHMoCiAgICBwYWNrYWdlID0gY29sX2NoYXJhY3RlcigpLAogICAgdmVyc2lvbiA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIGRlcGVuZHMgPSBjb2xfY2hhcmFjdGVyKCksCiAgICBpbXBvcnRzID0gY29sX2NoYXJhY3RlcigpLAogICAgbGljZW5zZSA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIG5lZWRzX2NvbXBpbGF0aW9uID0gY29sX2xvZ2ljYWwoKSwKICAgIGF1dGhvciA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIGJ1Z19yZXBvcnRzID0gY29sX2NoYXJhY3RlcigpLAogICAgdXJsID0gY29sX2NoYXJhY3RlcigpLAogICAgZGF0ZV9wdWJsaXNoZWQgPSBjb2xfZGF0ZShmb3JtYXQgPSAiJVktJW0tJWQiKSwKICAgIGRlc2NyaXB0aW9uID0gY29sX2NoYXJhY3RlcigpLAogICAgdGl0bGUgPSBjb2xfY2hhcmFjdGVyKCkKICApCikKYGBgCgojIyBRdWljayBWaWV3IHsudGFic2V0fQoKYGBge3J9CmRwbHlyOjpnbGltcHNlKG92X2R0LCAxMDApCmBgYAoKCgojIERhdGEgUXVhbGl0eQoKYGBge3J9Cm92X2R0IHw+IAogIGRwbHlyOjphcnJhbmdlKGRhdGVfcHVibGlzaGVkKSB8PiAKICB2aXNfbWlzcygpCmBgYAoKIyBRdWVzdGlvbnMKCiogSG93IGxvbmcgZGlkIHBhY2thZ2VzIHRha2UgZnJvbSB0aGVpciBmaXJzdCByZWxlYXNlIHRvIHZlcnNpb24gMS4wPyAKKiBXaGF0IHR5cGUgb2YgcGFja2FnZXMgd2VyZSBtb3N0IGZyZXF1ZW50IGluIGRpZmZlcmVudCB5ZWFycz8KKiBXaG8gYXJlIHRoZSBtb3N0IHByb2R1Y3RpdmUgYXV0aG9ycz8gCiogQ2FuIHlvdSBwcmVkaWN0IHRoZSBncm93dGggdG93YXJkIDIwMjU/CiogV2hhdCBsaWNlbnNlIGlzIG1vc3QgdXNlZD8gSGFzIHRoZXJlIGJlZW4gYSBjaGFuZ2Ugb3ZlciB0aW1lPwoqIEhvdyBtYW55IHBhY2thZ2VzIHVzZSBhbGwgQ0FQUywgYWxsIHNtYWxsLCBvciBhIG1peHR1cmU/CiogSG93IGhhdmUgdGhlIGRlcGVuZGVuY2llcyAmIGltcG9ydHMgY2hhbmdlZCBvdmVyIHRpbWU/CiogV2hpY2ggcmVwb3NpdG9yaWVzIGRvIHBhY2thZ2VzIHVzZT8gR2l0aHViL0JpdGJ1Y2tldCBldGMuIEhvdyBkbyB0aGVzZSB2YXJ5IG92ZXIgdGltZT8KKiBEbyBwYWNrYWdlcyBoYXZlIFVSTHMgZm9yIGJ1ZyByZXBvcnRzPwoqIElzIHRoZXJlIGFueSB0ZW1wb3JhbCBwYXR0ZXJucyB0byB3aGVuIHZlcnNpb25zIGFyZSBzdWJtaXR0ZWQgdG8gQ1JBTj8KKiBIYXZlIHRpdGxlcyAmIGRlc2NyaXB0aW9ucyBnb3R0ZW4gbG9uZ2VyIG92ZXIgdGltZT8KKiBEbyBhdXRob3JzIHVzZSBtaW5vciB2ZXJzaW9ucz8KCiMjIEZlYXR1cmVzCgpTZXBhcmF0ZSB2ZXJzaW9uCgpgYGB7cn0Kb3ZfZHQgPC0gb3ZfZHQgfD4KICBzZXBhcmF0ZSgKICAgIHZlcnNpb24sCiAgICBpbnRvID0KICAgICAgYygibWFqb3IiLCAibWlub3IiLCAicGF0Y2giKSwKICAgIHNlcCA9ICJcXC4iLAogICAgZXh0cmEgPSAibWVyZ2UiLAogICAgZmlsbCA9ICJyaWdodCIsCiAgICByZW1vdmUgPSBGQUxTRQogICkKYGBgCgoKTnVtYmVyIG9mIGRlcGVuZGVuY2llcwoKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+IAogIG11dGF0ZSgKICAgIG51bV9kZXAgPSBwdXJycjo6bWFwX2ludCgKICAgICAgLnggPSBkZXBlbmRzLAogICAgICAuZiA9IGZ1bmN0aW9uKHgpewogICAgICAgIHggfD4gCiAgICAgICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIiwiLCBzaW1wbGlmeSA9IFRSVUUpIHw+IAogICAgICAgICAgbGVuZ3RoKCkKICAgICAgfQogICAgKSwKICAgIG51bV9kZXAgPSBpZmVsc2UoaXMubmEoZGVwZW5kcyksIDAsIG51bV9kZXApCiAgKQpgYGAKCk51bWJlciBvZiBpbXBvcnRzCgpgYGB7cn0Kb3ZfZHQgPC0gb3ZfZHQgfD4gCiAgbXV0YXRlKAogICAgbnVtX2ltcG9ydHMgPSBwdXJycjo6bWFwX2ludCgKICAgICAgLnggPSBpbXBvcnRzLAogICAgICAuZiA9IGZ1bmN0aW9uKHgpewogICAgICAgIHggfD4gCiAgICAgICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIiwiLCBzaW1wbGlmeSA9IFRSVUUpIHw+IAogICAgICAgICAgbGVuZ3RoKCkKICAgICAgfQogICAgKSwKICAgIG51bV9pbXBvcnRzID0gaWZlbHNlKGlzLm5hKGltcG9ydHMpLCAwLCBudW1faW1wb3J0cykKICApCmBgYAoKTnVtYmVyIG9mIGF1dGhvcnMKCmBgYHtyfQpvdl9kdCA8LSBvdl9kdCB8PiAKICBtdXRhdGUoCiAgICBudW1fYXV0aG9ycyA9IHB1cnJyOjptYXBfaW50KAogICAgICAueCA9IGF1dGhvciwKICAgICAgLmYgPSBmdW5jdGlvbih4KXsKICAgICAgICB4IHw+IAogICAgICAgICAgc3RyaW5ncjo6c3RyX3NwbGl0KCIsIiwgc2ltcGxpZnkgPSBUUlVFKSB8PiAKICAgICAgICAgIGxlbmd0aCgpCiAgICAgIH0KICAgICkKICApCmBgYAoKVGVtcG9yYWwgZmVhdHVyZXMKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+IAogIG11dGF0ZSgKICAgIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZGF0ZV9wdWJsaXNoZWQpLAogICAgbW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKGRhdGVfcHVibGlzaGVkLCBsYWJlbCA9IFRSVUUpLAogICAgZGF5ID0gbHVicmlkYXRlOjpkYXkoZGF0ZV9wdWJsaXNoZWQpLAogICAgd2RheSA9IGx1YnJpZGF0ZTo6d2RheShkYXRlX3B1Ymxpc2hlZCwgbGFiZWwgPSBUUlVFKSwKICAgIHlyX21vbiA9IHNwcmludGYoIiVkLSVzIiwgeWVhciwgbW9udGgpLAogICAgZHQgPSBsdWJyaWRhdGU6OnltKHBhc3RlMCh5ZWFyLCAiLSIsIG1vbnRoKSkKICApCmBgYAoKClRpdGxlICYgRGVzY3JpcHRpb24gTGVuZ3RocwoKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+CiAgbXV0YXRlKAogICAgbGVuX3RpdGxlID0gcHVycnI6Om1hcF9pbnQodGl0bGUsIH4gc3RyaW5ncjo6c3RyX2NvdW50KC54LCAiXFx3KyIpKSwKICAgIGxlbl9kZXNjID0gcHVycnI6Om1hcF9pbnQoZGVzY3JpcHRpb24sIH4gc3RyaW5ncjo6c3RyX2NvdW50KC54LCAiXFx3KyIpKQogICkKYGBgCgoKTGljZW5zZSAKCmBgYHtyfQpvdl9kdCA8LSBvdl9kdCB8PiAKICBtdXRhdGUoCiAgICBsaWNlbnNlX2NsZWFuZWQgPSBjYXNlX3doZW4oCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5HUEwtMyIpIH4gIkdQTC0zIiwKICAgICAgc3RyaW5ncjo6c3RyX2RldGVjdChsaWNlbnNlLCAiXkdQTFxcc1xcKFtcXHNcXGRcXC48PT5dKjMiKSB+ICJHUEwtMyIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5HUEwtMiIpIH4gIkdQTC0yIiwKICAgICAgc3RyaW5ncjo6c3RyX2RldGVjdChsaWNlbnNlLCAiXkdQTFxcc1xcKFtcXHNcXGRcXC48PT5dKjIiKSB+ICJHUEwtMiIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5BR1BMIikgfiAiQUdQTCIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5MR1BMIikgfiAiTEdQTCIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIkFwYWNoZSIpIH4gIkFwYWNoZSIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIkJTRCIpIH4gIkJTRCIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIkxHUEwiKSB+ICJMR1BMIiwKICAgICAgIyBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJHTlUiKSB+ICJHTlUiLAogICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJNSVQiKSB+ICJNSVQiLAogICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJDQzAiKSB+ICJDQzAiLAogICAgICAjIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIk1QTCIpIH4gIk1QTCIsCiAgICAgICMgc3RyaW5ncjo6c3RyX2RldGVjdChsaWNlbnNlLCAiVW5saW1pdGVkIikgfiAiVW5saW1pdGVkIiwKICAgICAgIyBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJeQ0MiKSB+ICJDQyIsCiAgICAgIGxpY2Vuc2UgPT0gIkdQTCIgfiAiR1BMIiwKICAgICAgVFJVRSB+ICJPdGhlciIKICAgICAgKQogICkKYGBgCgpCdWcgUmVwb3J0IERvbWFpbgoKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+CiAgbXV0YXRlKGRvbWFpbiA9IHB1cnJyOjptYXBfY2hyKGJ1Z19yZXBvcnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXMubmEoLngpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKCIiKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybih1cmx0b29sczo6dXJsX3BhcnNlKC54KSRkb21haW4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pKQpgYGAKCgojIFRlbXBvcmFsIFF1ZXN0aW9ucwoKKiBIb3cgbG9uZyBkaWQgcGFja2FnZXMgdGFrZSBmcm9tIHRoZWlyIGZpcnN0IHJlbGVhc2UgdG8gdmVyc2lvbiAxLjA/IAoqIFdoYXQgdHlwZSBvZiBwYWNrYWdlcyB3ZXJlIG1vc3QgZnJlcXVlbnQgaW4gZGlmZmVyZW50IHllYXJzPwoqIFdobyBhcmUgdGhlIG1vc3QgcHJvZHVjdGl2ZSBhdXRob3JzPyAKKiBDYW4geW91IHByZWRpY3QgdGhlIGdyb3d0aCB0b3dhcmQgMjAyNT8KKiBXaGF0IGxpY2Vuc2UgaXMgbW9zdCB1c2VkPyBIYXMgdGhlcmUgYmVlbiBhIGNoYW5nZSBvdmVyIHRpbWU/IC0gZG9uZQoqIEhvdyBtYW55IHBhY2thZ2VzIHVzZSBhbGwgQ0FQUywgYWxsIHNtYWxsLCBvciBhIG1peHR1cmU/CiogSG93IGhhdmUgdGhlIGRlcGVuZGVuY2llcyAmIGltcG9ydHMgY2hhbmdlZCBvdmVyIHRpbWU/CiogV2hpY2ggcmVwb3NpdG9yaWVzIGRvIHBhY2thZ2VzIHVzZT8gR2l0aHViL0JpdGJ1Y2tldCBldGMuIEhvdyBkbyB0aGVzZSB2YXJ5IG92ZXIgdGltZT8KKiBEbyBwYWNrYWdlcyBoYXZlIFVSTHMgZm9yIGJ1ZyByZXBvcnRzPwoqIElzIHRoZXJlIGFueSB0ZW1wb3JhbCBwYXR0ZXJucyB0byB3aGVuIHZlcnNpb25zIGFyZSBzdWJtaXR0ZWQgdG8gQ1JBTj8KKiBEbyBhdXRob3JzIHVzZSBtaW5vciB2ZXJzaW9ucz8KKiBIYXZlIHRpdGxlcyAmIGRlc2NyaXB0aW9ucyBnb3R0ZW4gbG9uZ2VyIG92ZXIgdGltZT8gLSBkb25lCgoKKiBIb3cgaGF2ZSB0aGUgZGVwZW5kZW5jaWVzICYgaW1wb3J0cyBjaGFuZ2VkIG92ZXIgdGltZT8KCmBgYHtyfQpvdl9kdCB8PiAKICAgIGdyb3VwX2J5KGR0KSB8PiAKICAgIHN1bW1hcmlzZV9hdCh2YXJzKG51bV9kZXAsIG51bV9pbXBvcnRzKSwgbGlzdChtZWFuID0gbWVhbikpIHw+IAogIGdncGxvdChhZXMoeD0gZHQpKSArCiAgICBnZW9tX2ppdHRlcihhZXMoeSA9IG51bV9kZXBfbWVhbiwgY29sb3IgPSAibnVtX2RlcF9tZWFuIiksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9zbW9vdGgoYWVzKHkgPSBudW1fZGVwX21lYW4sIGNvbG9yID0gIm51bV9kZXBfbWVhbiIpLCBzcGFuID0gMC4zLCBzZSA9IEZBTFNFKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHkgPSBudW1faW1wb3J0c19tZWFuLCBjb2xvciA9ICJudW1faW1wb3J0c19tZWFuIiksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9zbW9vdGgoYWVzKHkgPSBudW1faW1wb3J0c19tZWFuLCBjb2xvciA9ICJudW1faW1wb3J0c19tZWFuIiksIHNwYW4gPSAwLjMsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpCmBgYAoKKiBIYXZlIHRpdGxlcyAmIGRlc2NyaXB0aW9ucyBnb3R0ZW4gbG9uZ2VyIG92ZXIgdGltZT8gCgpgYGB7cn0Kb3ZfZHQgfD4gCiAgICBncm91cF9ieShkdCkgfD4gCiAgICBzdW1tYXJpc2VfYXQodmFycyhsZW5fdGl0bGUsIGxlbl9kZXNjKSwgbGlzdChtZWRpYW4gPSBtZWRpYW4sIHNkID0gc2QpLCBuYS5ybSA9IFRSVUUpIHw+IAogIGdncGxvdChhZXMoeD0gZHQpKSArCiAgICBnZW9tX2ppdHRlcihhZXMoeSA9IGxlbl90aXRsZV9tZWRpYW4sIGNvbG9yID0gImxlbl90aXRsZV9tZWRpYW4iKSwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChhZXMoeSA9IGxlbl90aXRsZV9tZWRpYW4sIGNvbG9yID0gImxlbl90aXRsZV9tZWRpYW4iKSwgc3BhbiA9IDAuMywgc2UgPSBGQUxTRSkgKwogIGdlb21faml0dGVyKGFlcyh5ID0gbGVuX2Rlc2NfbWVkaWFuLCBjb2xvciA9ICJsZW5fZGVzY19tZWRpYW4iKSwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChhZXMoeSA9IGxlbl9kZXNjX21lZGlhbiwgY29sb3IgPSAibGVuX2Rlc2NfbWVkaWFuIiksIHNwYW4gPSAwLjMsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpCmBgYAoKYGBge3J9Cm92X2R0IHw+IAogIGZpbHRlcih5ZWFyICVpbiUgYygyMDIyLCAyMDIwLCAyMDE4KSkgfD4gCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxlbl9kZXNjLCAKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBhcy5mYWN0b3IoeWVhciksIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBhcy5mYWN0b3IoeWVhcikKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIGFscGhhID0gMC4zCiAgICAgICAgICAgICAgICkKb3ZfZHQgfD4gCiAgZmlsdGVyKHllYXIgJWluJSBjKDIwMjIsIDIwMjAsIDIwMTgpKSB8PiAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBsZW5fZGVzYywgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYXMuZmFjdG9yKHllYXIpLCAKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYXMuZmFjdG9yKHllYXIpCiAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMwogICAgICAgICAgICAgICApICsKICBmYWNldF93cmFwKH55ZWFyKQpgYGAKCgoKYGBge3J9Cm92X2R0IHw+IAogIGdncGxvdChhZXMoeD0gZGF0ZV9wdWJsaXNoZWQsIHkgPSBsZW5fdGl0bGUpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjA1KSArCiAgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuMSwgc2UgPSBGQUxTRSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX3lfbG9nMTAoKQoKb3ZfZHQgfD4gCiAgZ2dwbG90KGFlcyh4PSBkYXRlX3B1Ymxpc2hlZCwgeSA9IGxlbl9kZXNjKSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4wNSkgKwogIGdlb21fc21vb3RoKHNwYW4gPSAwLjIsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KCmBgYAoKCiogV2hhdCBsaWNlbnNlIGlzIG1vc3QgdXNlZD8gSGFzIHRoZXJlIGJlZW4gYSBjaGFuZ2Ugb3ZlciB0aW1lPwoKYGBge3J9Cm92X2R0IHw+IAogIGdyb3VwX2J5KGxpY2Vuc2VfY2xlYW5lZCkgfD4gCiAgY291bnQoKSB8PiAKICBnZ3Bsb3QoYWVzKHggPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihsaWNlbnNlX2NsZWFuZWQsIG4pLCB5ID0gbiwgZmlsbCA9IGxpY2Vuc2VfY2xlYW5lZCkpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICIiLCB5ID0gIiIpCmBgYAoKYGBge3J9Cm92X2R0IHw+IAogIGdyb3VwX2J5KGR0KSB8PiAKICBjb3VudChsaWNlbnNlX2NsZWFuZWQpIHw+IAogIG11dGF0ZShsaWNlbnNlX2NsZWFuZWQgPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihsaWNlbnNlX2NsZWFuZWQsIG4pKSB8PiAKICBnZ3Bsb3QoYWVzKHg9IGR0LCB5ID0gbiwgY29sb3IgPSBsaWNlbnNlX2NsZWFuZWQpKSArCiAgIyBnZW9tX2xpbmUoIGFscGhhID0gMC4zKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjMpICsKICBnZW9tX3Ntb290aChzcGFuID0gMC4zLCBzZSA9IEZBTFNFKSArCiAgdGhlbWVfbGlnaHQoKQpvdl9kdCB8PiAKICBncm91cF9ieShkdCkgfD4gCiAgY291bnQobGljZW5zZV9jbGVhbmVkKSB8PiAKICBtdXRhdGUobGljZW5zZV9jbGVhbmVkID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIobGljZW5zZV9jbGVhbmVkLCBuKSkgfD4gCiAgZ2dwbG90KGFlcyh4PSBkdCwgeSA9IG4sIGNvbG9yID0gbGljZW5zZV9jbGVhbmVkKSkgKwogICMgZ2VvbV9saW5lKCBhbHBoYSA9IDAuMykgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4zKSArCiAgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuOCwgc2UgPSBGQUxTRSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX3lfbG9nMTAoKQpgYGAKCiogRG8gcGFja2FnZXMgaGF2ZSBVUkxzIGZvciBidWcgcmVwb3J0cz8KCgpgYGB7cn0Kb3ZfZHQgfD4gCiAgZ3JvdXBfYnkoZHQpIHw+IAogIGNvdW50KHVybF9leGlzdCA9IGlzLm5hKHVybCkpIHw+ICAKICBnZ3Bsb3QoYWVzKHg9IGR0LCB5ID0gbiwgY29sb3IgPSB1cmxfZXhpc3QpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjMpICsKICBnZW9tX3Ntb290aChzcGFuID0gMC4zLCBzZSA9IEZBTFNFKSArCiAgdGhlbWVfbGlnaHQoKQpvdl9kdCB8PiAKICBncm91cF9ieShkdCkgfD4gCiAgY291bnQodXJsX2V4aXN0ID0gaXMubmEoYnVnX3JlcG9ydHMpKSB8PiAgCiAgZ2dwbG90KGFlcyh4PSBkdCwgeSA9IG4sIGNvbG9yID0gdXJsX2V4aXN0KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4zKSArCiAgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuMywgc2UgPSBGQUxTRSkgKwogIHRoZW1lX2xpZ2h0KCkKYGBgCgoqIFdoaWNoIHJlcG9zaXRvcmllcyBkbyBwYWNrYWdlcyB1c2U/IEdpdGh1Yi9CaXRidWNrZXQgZXRjLiBIb3cgZG8gdGhlc2UgdmFyeSBvdmVyIHRpbWU/CgpgYGB7cn0Kb3ZfZHQgfD4gCiAgZmlsdGVyKGRvbWFpbiAhPSAiIikgfD4gCiAgbXV0YXRlKGRvbWFpbiA9IGZvcmNhdHM6OmZjdF9sdW1wX21pbihkb21haW4sIDIwKSkgfD4gCiAgZ3JvdXBfYnkoZHQpIHw+IAogIGNvdW50KGRvbWFpbikgfD4gIAogIGdncGxvdChhZXMoeD0gZHQsIHkgPSBuLCBjb2xvciA9IGRvbWFpbikpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMykgKwogIGdlb21fc21vb3RoKHNwYW4gPSAwLjUsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpCmBgYAoKKiBJcyB0aGVyZSBhbnkgdGVtcG9yYWwgcGF0dGVybnMgdG8gd2hlbiB2ZXJzaW9ucyBhcmUgc3VibWl0dGVkIHRvIENSQU4/CgpgYGB7cn0Kb3ZfZHQgfD4gCiAgZ3JvdXBfYnkoZHQpIHw+IAogIGNvdW50KCkgfD4gCiAgZ2dwbG90KGFlcyhkdCwgbikpICsKICBnZW9tX2xpbmUoKQoKb3ZfZHQgfD4gCiAgZmlsdGVyKCFpcy5uYShkdCkpIHw+IAogIGNvdW50KGR0KSB8PiAKICBhcnJhbmdlKGR0KSB8PiAKICB0aW1ldGs6OnBhZF9ieV90aW1lKC5ieSA9ICJtb250aCIsIC5wYWRfdmFsdWUgPSAwKSAtPiB4ZGF0CnRpbWV0azo6cGxvdF9zZWFzb25hbF9kaWFnbm9zdGljcyh4ZGF0LCBkdCwgbikKYGBgCgo=